#include "cserver.h"
#include "cclient.h"
#include "cbuffer.h"
#include <Arduino.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <lwip/sockets.h>
#include <sys/select.h>
#include <sys/errno.h>
using namespace std;

CServer::CServer(const char * ip, int port, int backlog, int client_send_buffer_size, int client_recv_buffer_size){
    m_client_send_buffer_size = client_send_buffer_size;
    m_client_recv_buffer_size = client_recv_buffer_size;
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_addr.s_addr = inet_addr(ip);
    server_addr.sin_port = htons(port);
    server_addr.sin_family = AF_INET;
    //创建服务端socket
    m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
    if(m_sockfd < 0){
        return;
    }
    int flag = 1;
    if(-1 == setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int))){
        return;
    }
    if(-1 == bind(m_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr))){
        return;
    }
    if(-1 == listen(m_sockfd, 128)){
        return;
    }
}

CServer::~CServer(){
    if(m_sockfd > 0){
        close(m_sockfd);
        m_sockfd = -1;
    }
}

void CServer::onClientJoin(CClient * client){
    Serial.printf("client %s:%d join\n", client->ip(), client->port());
    client->senddata("hello client", 13);
}

void CServer::onClientLeave(CClient * client){
    Serial.printf("client %s:%d leave\n", client->ip(), client->port());
}

CClient * CServer::accept(){
    static struct sockaddr_in client_addr;
    static socklen_t client_addr_len = sizeof(client_addr);
    int client_fd = ::accept(m_sockfd, (struct sockaddr*)&client_addr, &client_addr_len);
    if(client_fd < 0){
        Serial.printf("accept error, errno = %d\n", errno);
        return nullptr;
    }
    CClient * client = new CClient(client_fd, m_client_send_buffer_size, m_client_recv_buffer_size);
    char * ip = inet_ntoa(client_addr.sin_addr);
    int port = ntohs(client_addr.sin_port);
    client->ip(ip);
    client->port(port);
    m_clients.insert(client);
    m_clients_has_change = true;
    onClientJoin(client);
    return client;
}

void CServer::remove_client(CClient * client){
    m_clients.erase(client);
    delete client;
    onClientLeave(client);
}

void CServer::main(){
    //读事件设置
    if(m_clients_has_change){
        m_clients_has_change = false;
        FD_ZERO(&m_read_set);
        FD_SET(m_sockfd, &m_read_set);
        m_max_fd = m_sockfd;
        for(auto & client : m_clients){
            int cur_sockfd = client->socket();
            FD_SET(cur_sockfd, &m_read_set);
            if(m_max_fd < cur_sockfd){
                m_max_fd = cur_sockfd;
            }
        }
        //m_read_set_bek = m_read_set;
        memcpy(&m_read_set_bek, &m_read_set, sizeof(m_read_set));
    }else{
        //m_read_set = m_read_set_bek;
        memcpy(&m_read_set, &m_read_set_bek, sizeof(m_read_set));
    }

    //写事件设置
    FD_ZERO(&m_write_set);
    for(auto & client : m_clients){
        if(!client->has_send_data()){
            continue;
        }
        FD_SET(client->socket(), &m_write_set);
    }

    Serial.println("start select");
    //io复用
    int n_ret = select(m_max_fd + 1, &m_read_set, &m_write_set, nullptr, nullptr);

    if(n_ret < 0){
        if(errno == EINTR){
            return;
        }
        //出了错误
        Serial.println("select error");
        return;
    }else if(n_ret == 0){
        return;
    }

    //读事件
    if(FD_ISSET(m_sockfd, &m_read_set)){
        //服务端读事件
        accept();
    }
    
    for(auto iter = m_clients.begin(); iter != m_clients.end(); ){
        int cur_socket = (*iter)->socket();
        if(FD_ISSET(cur_socket, &m_read_set)){
            //客户端读事件
            if((*iter)->recvdata() <= 0){
                //客户端离开或者出错
                auto iter_bek = iter;
                ++iter;
                onClientLeave(*iter_bek);
                delete (*iter_bek);
                m_clients.erase(iter_bek);
                m_clients_has_change = true;
                continue;
            }
        }
        ++iter;
    }

    //写事件
    for(auto iter = m_clients.begin(); iter != m_clients.end();){
        int cur_socket = (*iter)->socket();
        if(FD_ISSET(cur_socket, &m_write_set)){
            if((*iter)->senddata() <= 0){
                //客户端写错误
                auto iter_bek = iter;
                ++iter;
                onClientLeave(*iter_bek);
                delete (*iter_bek);
                m_clients.erase(iter_bek);
                m_clients_has_change = true;
                continue;
            }
        }
        ++iter;
    }

    //处理接收缓冲区数据
    for(auto & client : m_clients){
        if(!client->has_recv_data()){
            continue;
        }
        CBuffer * buffer = client->recv_buffer();
        buffer->c_str()[buffer->size()] = 0;
        Serial.printf("server recv from client[%s:%d]:%s\n", client->ip(), client->port(), buffer->c_str());
        client->senddata(buffer);
        buffer->clear();
    }
}
